#include <asn_internal.h>
#include <constraints.h>

int asn_generic_no_constraint(
    const asn_TYPE_descriptor_t* type_descriptor, const void* struct_ptr,
    asn_app_constraint_failed_f* cb, void* key) {
  (void) type_descriptor; /* Unused argument */
  (void) struct_ptr;      /* Unused argument */
  (void) cb;              /* Unused argument */
  (void) key;             /* Unused argument */

  /* Nothing to check */
  return 0;
}

int asn_generic_unknown_constraint(
    const asn_TYPE_descriptor_t* type_descriptor, const void* struct_ptr,
    asn_app_constraint_failed_f* cb, void* key) {
  (void) type_descriptor; /* Unused argument */
  (void) struct_ptr;      /* Unused argument */
  (void) cb;              /* Unused argument */
  (void) key;             /* Unused argument */

  /* Unknown how to check */
  return 0;
}

struct errbufDesc {
  const asn_TYPE_descriptor_t* failed_type;
  const void* failed_struct_ptr;
  char* errbuf;
  size_t errlen;
};

static void _asn_i_ctfailcb(
    void* key, const asn_TYPE_descriptor_t* td, const void* sptr,
    const char* fmt, ...) {
  struct errbufDesc* arg = key;
  va_list ap;
  ssize_t vlen;
  ssize_t maxlen;

  arg->failed_type       = td;
  arg->failed_struct_ptr = sptr;

  maxlen = arg->errlen;
  if (maxlen <= 0) return;

  va_start(ap, fmt);
  vlen = vsnprintf(arg->errbuf, maxlen, fmt, ap);
  va_end(ap);
  if (vlen >= maxlen) {
    arg->errbuf[maxlen - 1] = '\0';       /* Ensuring libc correctness */
    arg->errlen             = maxlen - 1; /* Not counting termination */
    return;
  } else if (vlen >= 0) {
    arg->errbuf[vlen] = '\0'; /* Ensuring libc correctness */
    arg->errlen       = vlen; /* Not counting termination */
  } else {
    /*
     * The libc on this system is broken.
     */
    vlen = sizeof("<broken vsnprintf>") - 1;
    maxlen--;
    arg->errlen = vlen < maxlen ? vlen : maxlen;
    memcpy(arg->errbuf, "<broken vsnprintf>", arg->errlen);
    arg->errbuf[arg->errlen] = 0;
  }

  return;
}

int asn_check_constraints(
    const asn_TYPE_descriptor_t* type_descriptor, const void* struct_ptr,
    char* errbuf, size_t* errlen) {
  struct errbufDesc arg;
  int ret;

  arg.failed_type       = 0;
  arg.failed_struct_ptr = 0;
  arg.errbuf            = errbuf;
  arg.errlen            = errlen ? *errlen : 0;

  ret = type_descriptor->encoding_constraints.general_constraints(
      type_descriptor, struct_ptr, _asn_i_ctfailcb, &arg);
  if (ret == -1 && errlen) *errlen = arg.errlen;

  return ret;
}
